/*
 *  Copyright (C) 2011 JiangHongTiao <jjurco.sk_gmail.com>
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package sk.lieskove.jianghongtiao.common.utils;

import java.util.logging.Level;
import org.apache.log4j.Logger;

import java.io.*;
import java.util.InvalidPropertiesFormatException;
import java.util.Properties;

// import log4j packages
/**
 * Date of create: May 15, 2011
 *
 * @author JiangHongTiao <jjurco.sk_gmail.com>
 * @version 2011.0515
 */
public class PropertiesUtils implements Serializable {

    private static Logger log = Logger.getLogger(
            PropertiesUtils.class.getClass());
    private Properties properties = new Properties();
    private Properties weak = new Properties();
    private Properties forClass = new Properties();
    private Properties strong = new Properties();

    public enum PROPERTIES_TYPE {

        WEAK, FOR_CLASS, STRONG
    };
    private File weakFile = null;
    private File strongFile = null;
    private File forClassFile = null;
    private Class propertiesForClass = null;

    /**
     * create new instance of properties for specific class.
     * @param propertiesForClass load weak, Class and strong properties. variable can be null
     */
    public PropertiesUtils(Class propertiesForClass) {
        this.propertiesForClass = propertiesForClass;
        load();
    }

    private void load() {
        properties = new Properties();
        weak = new Properties();
        forClass = new Properties();
        strong = new Properties();
        log.debug("Using working directory: " + System.getProperty("user.dir"));
        File test = new File("settings/general-weak-settings.xml");
        File testSub = new File("../settings/general-weak-settings.xml");
        String prefix = "";
        if (!test.exists()) {
            prefix = "../";
        }
        if (test.exists() || testSub.exists()) {
            try {
                log.debug("loading settings file: '" + prefix
                        + "settings/general-weak-settings.xml'");
                weakFile = new File(prefix
                        + "settings/general-weak-settings.xml");
                weak.loadFromXML(new FileInputStream(weakFile));
            } catch (FileNotFoundException ex) {
                log.error(prefix
                        + "settings/general-weak-settings.xml settings file not found."
                        + "Actual working directory is: " + System.getProperty("user.dir"));
            } catch (InvalidPropertiesFormatException ex) {
                log.error(prefix + "settings/general-weak-settings.xml settings "
                        + "file is corrupted");
            } catch (IOException ex) {
                log.error(
                        "exception thrown while reading file: " + prefix
                        + "settings/general-weak-settings.xml");
            }
            try {
                if (propertiesForClass != null) {
                    forClassFile = new File(prefix + "settings/" + propertiesForClass.getName() + ".xml");
                    if (forClassFile.exists()) {
                        log.debug("Loading properties file for class: "
                                + forClassFile.getName());
                        forClass.loadFromXML(new FileInputStream(forClassFile));
                    } else {
                        log.debug("Loading file for class: File for class '"
                                + forClassFile.getName() + "' does not exists");
                    }
                } else {
                    log.debug("Loading file for class: Class is null.");
                }
            } catch (FileNotFoundException ex) {
                log.error(prefix
                        + "settings/" + propertiesForClass.getName()
                        + ".xml settings file not found");
            } catch (InvalidPropertiesFormatException ex) {
                log.error(prefix
                        + "settings/" + propertiesForClass.getName()
                        + ".xml settings file is corrupted");
            } catch (IOException ex) {
                log.error("exception thrown while reading file: " + prefix
                        + "settings/" + propertiesForClass.getName() + ".xml");
            }
            try {
                strongFile = new File(prefix
                        + "settings/general-strong-settings.xml");
                strong.loadFromXML(new FileInputStream(strongFile));
            } catch (FileNotFoundException ex) {
                log.error(prefix
                        + "settings/general-strong-settings.xml settings file not found");
            } catch (InvalidPropertiesFormatException ex) {
                log.error(prefix
                        + "settings/general-strong-settings.xml settings file is corrupted");
            } catch (IOException ex) {
                log.error(
                        "exception thrown while reading file: " + prefix
                        + "settings/general-strong-settings.xml");
            }
            properties.putAll(weak);
            properties.putAll(forClass);
            properties.putAll(strong);

        } else {
            log.error("Cannot find settings directory");
        }
    }

    public String getProperty(String key) {
        return properties.getProperty(key, null);
    }

    public Boolean isEmpty() {
        return properties.isEmpty();
    }

    public String getProperty(String key, String defaultValue) {
        return properties.getProperty(key, defaultValue);
    }

    public void setProperty(String key, String value) {
        properties.setProperty(key, value);
    }

    public static String pathBuilder(String path, String folder, String ext) {
        return folder + "/" + path + ext;
    }

    public static PropertiesUtils loadFile(File file) {
        PropertiesUtils pu = new PropertiesUtils(null);
        try {
            pu.properties.load(new FileInputStream(file));
        } catch (IOException ex) {
            log.error("Loading of file: '" + file.getName() + "' failed", ex);
        }
        return pu;
    }

    public static PropertiesUtils loadXMLFile(File file) {
        PropertiesUtils pu = new PropertiesUtils(null);
        try {
            pu.properties.loadFromXML(new FileInputStream(file));
        } catch (InvalidPropertiesFormatException ex) {
            log.error("Loaded properties file '" + file.getName()
                    + "' has invalid format", ex);
        } catch (IOException ex) {
            log.error("Exception thrown while file '" + file.getName()
                    + "' was loaded. ", ex);
        }
        return pu;
    }

    public static PropertiesUtils load(String path, String folder, String ext) {
        return loadFile(new File(pathBuilder(path, folder, ext)));
    }

    public static PropertiesUtils loadXML(String path, String folder, String ext) {
        return loadXMLFile(new File(pathBuilder(path, folder, ext)));
    }

    /**
     * 
     * @param c
     * @return
     */
    public static PropertiesUtils loadResForClass(Class c) {
        return load(c.getCanonicalName().substring(26), "resources/classes/",
                ".properties");
    }

    /**
     * load resource file. File must be located in 'resources/' directory
     * @param fileNameS filename of resource file
     * @return returns loaded properties
     */
    public static PropertiesUtils loadRes(String fileNameS) {
        return load(fileNameS, "resources", "");
    }

    /**
     * load resource from XML document for specific class. Resource should be
     * located in directory 'resources/classes/[className].xml'
     * @param c load resources for class
     * @return PropertiesUtils for this class
     */
    public static PropertiesUtils loadResForClassXML(Class c) {
        return loadXML(c.getCanonicalName(), "resources/classes/", ".xml");
    }

    /**
     * load resource file form xml document. File must be located in 'resources/' directory
     * @param fileNameS filename of xml file
     * @return returns loaded properties
     */
    public static PropertiesUtils loadResFileXML(String fileNameS) {
        return loadXML(fileNameS, "resources", "");
    }

    /**
     * load config file for specific class. File with canonical name of the class
     * must be located in 'config/classes/'
     * @param c class of the properties
     * @return returns loaded properties
     */
    public static PropertiesUtils loadConfForClass(Class c) {
        return load(c.getCanonicalName(), "config/classes/", ".properties");
    }

    /**
     * load config file
     * @param fileNameS name of the properties file. File should be located in 'config/' file
     * @return returns loaded properties
     */
    public static PropertiesUtils loadConf(String fileNameS) {
        return load(fileNameS, "config", "");
    }

    /**
     * load config file for class. Config file should be located in 'config/classes/'
     * @param c load resources for this class
     * @return returns loaded properties
     */
    public static PropertiesUtils loadConfForClassXML(Class c) {
        return loadXML(c.getCanonicalName(), "config/classes/", ".xml");
    }

    /**
     * load config file from XML
     * @param fileNameS name of file WITH extension. File should be located in 'config/' directory with extension '.xml'
     * @return returns loaded properties
     */
    public static PropertiesUtils loadConfFileXML(String fileNameS) {
        return loadXML(fileNameS, "config", "");
    }

    /**
     * save properties to xml file
     * @param fileName filename
     * @param comment comment for file
     */
    public void saveXML(File fileName, String comment) {
        try {
            setProperty("author", "JiangHongTiao");
            setProperty("email", "jianghongtiao@gmail.com");
            setProperty("program", "paris");
            setProperty("version", "1.0");
            properties.storeToXML(new FileOutputStream(fileName), comment,
                    "utf-8");
        } catch (IOException ex) {
            log.error("Exception thrown while saving file: '"
                    + fileName.getName() + "'", ex);
        }
    }

    /**
     * save properties to xml file
     * @param file file
     */
    public void saveXML(File file) {
        saveXML(file, "");
    }

    /**
     * save properties to xml file
     * @param fileName filenames
     */
    public void saveXML(String fileName) {
        saveXML(new File(fileName), "");
    }

    /**
     * save properties to xml file
     * @param fileName filename
     * @param comment comment to file
     */
    public void saveXML(String fileName, String comment) {
        saveXML(new File(fileName), comment);
    }

    /**
     * save property to the file
     * @param type type of property to save
     * @param key 
     * @param value 
     */
    public void writeProperty(PROPERTIES_TYPE type, String key, String value) {
        Properties prop = null;
        File file = null;
        switch (type) {
            case FOR_CLASS:
                prop = forClass;
                file = forClassFile;
                break;
            case STRONG:
                prop = strong;
                file = strongFile;
                break;
            case WEAK:
                prop = strong;
                file = weakFile;
                break;
            default:
                throw new IllegalArgumentException(
                        "Unknown type PROPERTIES_TYPE: " + type);
        }
        prop.setProperty(key, value);
        try {
            prop.storeToXML(new FileOutputStream(file), null, "UTF-8");
        } catch (IOException ex) {
            java.util.logging.Logger.getLogger(PropertiesUtils.class.getName()).log(Level.SEVERE, null, ex);
        }
        load();
    }
}
